package com.qf.blog.controller;


import com.qf.blog.commons.constant.RedisKeys;
import com.qf.blog.commons.utils.Result;
import com.qf.blog.commons.utils.UserThreadLocal;
import com.qf.blog.dto.TokenDto;
import com.qf.blog.entity.Message;
import com.qf.blog.entity.User;
import com.qf.blog.service.MessageService;
import com.qf.blog.service.UserService;
import com.qf.blog.service.impl.FollowerServiceImpl;
import com.qf.blog.vo.FollowerVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * User: key
 * ClassName:FollowerController
 * Date: 2023/6/8
 * Time: 19:13
 * description:
 */
@Controller
@RequestMapping("/follower")
public class FollowerController {

    @Autowired
    private UserService userService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MessageService messageService;

    @Autowired
    private FollowerServiceImpl followerService;

    /*
     * 关注和取消关注操作
     * 后期再将逻辑放到业务层
     * */
    @RequestMapping("/click")
    @ResponseBody
    @CrossOrigin(value = "http://www.blog.com:8080/")
    public Result click(@RequestBody FollowerVo followerVo) {
        //判断用户是否进行登录
        TokenDto userToken = UserThreadLocal.get();
        if (userToken == null) {
            return Result.error("请先登录");
        }

        User user = userService.getById(followerVo.getUserId());

        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        //获取当前页面用户的  blog:follow:entityId
        String followerKey = String.format(RedisKeys.USER_FOLLOWER_KEY, followerVo.getEntityId());

        //获取当前页面用户的  blog:fans:entityId
        String entityFollowerKey = String.format(RedisKeys.USER_FANS_KEY, followerVo.getEntityId());

        //获取当前登录用户的  blog:follower:userId
        String currentFansKey = String.format(RedisKeys.USER_FOLLOWER_KEY, followerVo.getUserId());

        //获取当前登录用户的  blog:fans:userId
        String fansKey = String.format(RedisKeys.USER_FANS_KEY, followerVo.getUserId());

        //判断当前登录用户是否为此页面用户的粉丝
        Boolean flag = redisTemplate.hasKey(fansKey);

        //标记
        Double score = null;

        // 判断是否已经关注了，如果没有关注是没有分数的
        if (flag) {
            //为什么要获取entityId
            //这是因为fans里面的value就是关注的对象
            score = zset.score(fansKey, followerVo.getEntityId().toString());
        }

        /*
         * fans中存的是被关注的对象
         * follower中存的是粉丝对象
         * */
        Message message = messageService.selectByToAndFrom(followerVo.getEntityId(), followerVo.getUserId());
        //不为空说明已经进行过插入了，为空的情况则代表从来没有关注过，直接进行插入操作即可
        if (message != null) {
            Integer status = message.getStatus();
            if (status == 0) {
                // 取消关注
                //此逻辑要将登录用户的fans内移除当前页面用户的value
                zset.remove(fansKey, followerVo.getEntityId().toString());

                //此逻辑是要将当前页面用户的follower内的登录用户的value移除
                zset.remove(followerKey, followerVo.getUserId().toString());

                //将关注消息的状态改为2
                message.setFromId(followerVo.getUserId());
                message.setToId(followerVo.getEntityId());
                message.setContent(user.getUsername() + "对你取消了关注");
                message.setStatus(2);
                message.setCreateTime(new Date());
                messageService.updateOnFollow(message);
            } else {
                // 关注

                //事务
                redisTemplate.execute(new SessionCallback<Object>() {
                    @Override
                    public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                        redisTemplate.multi();
                        try {
                            //此逻辑内要将登录用户的fans里添加一个当前页面用户的value
                            zset.add(fansKey, followerVo.getEntityId().toString(), System.currentTimeMillis());
                            //此逻辑要将当前页面用户的follower内添加一个当前登录用户的value
                            zset.add(followerKey, followerVo.getUserId().toString(), System.currentTimeMillis());
                            redisTemplate.exec();
                        } catch (Exception e) {
                            redisTemplate.discard();
                        }
                        return null;
                    }
                });

                //将关注消息存入数据库
                message.setFromId(followerVo.getUserId());
                message.setToId(followerVo.getEntityId());
                message.setContent(user.getUsername() + "关注了你");
                message.setStatus(0);
                message.setCreateTime(new Date());
                messageService.updateOnFollow(message);
            }
            //如果没有分数，或者当前登录对象不是粉丝，则代表当前登录用户没有关注此页面用户
        } else {
            redisTemplate.execute(new SessionCallback<Object>() {
                @Override
                public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                    redisTemplate.multi();
                    try {
                        //此逻辑内要将登录用户的fans里添加一个当前页面用户的value
                        zset.add(fansKey, followerVo.getEntityId().toString(), System.currentTimeMillis());

                        //此逻辑要将当前页面用户的follower内添加一个当前登录用户的value
                        zset.add(followerKey, followerVo.getUserId().toString(), System.currentTimeMillis());
                        return redisTemplate.exec();
                    } catch (Exception e) {
                        redisTemplate.discard();
                    }
                    return null;
                }
            });

            //将关注消息存入数据库
            Message message1 = new Message();
            message1.setFromId(followerVo.getUserId());
            message1.setToId(followerVo.getEntityId());
            message1.setContent(user.getUsername() + "关注了你");
            message1.setStatus(0);
            message1.setConversationId("follow");
            message1.setCreateTime(new Date());
            messageService.insertOnFollow(message1);
        }
        return Result.ok("操作成功");
    }

    /*
     * 关注列表
     * 1、需要获取关注人的信息
     * 3、通过当前传入的用户的id可以查到redis中的关注人信息
     * 3、将关注人的信息封装进modelMap响应给前端
     * */
    @RequestMapping("/findFollowerListByUserId")
    public String findFollowerListByUserId(Integer entityId, ModelMap modelMap) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();

        //获取当前页面对象的id
        User user = userService.getById(entityId);

        //获取当前页面用户的 blog:fans:entity 中的value
        String followerKey = String.format(RedisKeys.USER_FANS_KEY, entityId);

        //判断当前页面用户的followerKey是否存在
        Boolean exist = redisTemplate.hasKey(followerKey);

        //定义一个List集合用于存关注对象
        List<User> followerList = new ArrayList<>();

        //如果存在，则获取所有关注对象
        if (exist) {
            //根据followerKey获取全部的集合内容
            Set<String> set = zset.range(followerKey, 0, -1);
            followerList = new ArrayList<>();
            if (set != null) {
                for (String s : set) {
                    Integer integer = Integer.valueOf(s);
                    User followeUser = userService.getById(integer);
                    Double score = zset.score(followerKey, s);
                    long l = Math.round(score);
                    Date date = new Date(l);
                    Long size = zset.size(followerKey);
                    followeUser.setCreateTime(date);
                    followerList.add(followeUser);
                }
            }
        }

        //将集合存入modelMap中
        modelMap.put("followerList", followerList);
        modelMap.put("userManager", user);
        return "site/followee";
    }

    /*
     * 粉丝列表
     * 1、需要获取粉丝的信息
     * 3、通过当前传入的用户的id可以查到redis中的粉丝信息
     * 3、将粉丝的信息封装进modelMap响应给前端
     * */
    @RequestMapping("/findFansListByUserId")
    public String findFansListByUserId(Integer entityId, ModelMap modelMap) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();

        //获取当前页面对象的id,通过id获取当前页面用户
        User user = userService.getById(entityId);

        //获取当前页面用户的 blog:follower:entity 中的value
        String followerKey = String.format(RedisKeys.USER_FOLLOWER_KEY, entityId);

        //判断当前页面用户的followerKey是否存在
        Boolean exist = redisTemplate.hasKey(followerKey);

        //定义一个List集合用于存粉丝对象
        List<User> fansList = new ArrayList<>();

        //如果存在，则获取所有关注对象
        if (exist) {
            //根据followerKey获取全部的集合内容
            Set<String> set = zset.range(followerKey, 0, -1);
            fansList = new ArrayList<>();
            if (set != null) {
                for (String s : set) {
                    Integer integer = Integer.valueOf(s);
                    User followeUser = userService.getById(integer);
                    //获取key中的分数值
                    Double score = zset.score(followerKey, s);
                    //将Double的分数值转换为日期格式存入关注对象中以获取关注时间
                    long l = Math.round(score);
                    Date date = new Date(l);
                    followeUser.setCreateTime(date);
                    fansList.add(followeUser);
                }
            }
        }

        //将集合存入modelMap中
        modelMap.put("fansList", fansList);
        modelMap.put("userManager", user);
        return "site/fans";
    }

    /*
     * 点击用户头像进入根据用户id进入该用户的首页
     * 进入用户页前进行一系列逻辑判断
     * 先跑通程序
     * 后期再将方法分化到业务层
     * */
    @RequestMapping("/info")
    public String info(Integer entityId, ModelMap modelMap) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        FollowerVo followerVo = new FollowerVo();

        //将当前登录用户id存入followerVo中
        followerVo.setUserId(userService.getUserByUsername(UserThreadLocal.get().getUsername()).getId());

        //获取点击头像进入的用户
        User entityUser = userService.getById(entityId);

        //将查看的用户的ID封装进followerVo中
        followerVo.setEntityId(entityId);

        //根据id获取想要查询的key，获取的key中存的是当前页面用户（entityId）的粉丝信息
        String followerKey = String.format(RedisKeys.USER_FOLLOWER_KEY, followerVo.getEntityId());

        //判断当前主页的用户是否有关注的人，获取的key是当前页面用户（entityId）的关注用户信息
        String entityKey = String.format(RedisKeys.USER_FANS_KEY, followerVo.getEntityId());

        //这一步获取的key是登录用户（userId）的粉丝信息（逻辑先写出来，万一用上了）
        String userKey = String.format(RedisKeys.USER_FOLLOWER_KEY, followerVo.getUserId());

        //这一步是判断当前用户是否为粉丝，获取的key中存的是登陆用户（userId）的关注用户信息
        String fansKey = String.format(RedisKeys.USER_FANS_KEY, followerVo.getUserId());

        //判断redis中是否有当前用户的key
        Boolean flag = redisTemplate.hasKey(followerKey);
        Boolean flag1 = redisTemplate.hasKey(fansKey);
        Boolean flag2 = redisTemplate.hasKey(entityKey);

        //标记
        Double score = null;
        Double fanScore = null;
        Long size = null;
        Long count = null;

        //如果有，则获取分数，根据分数是否为空判断是否关注了
        if (flag) {
            //获取粉丝分数，如果没有登录用户的value则分数为0
            score = zset.score(followerKey, followerVo.getUserId().toString());
            count = zset.size(followerKey);
        }

        //登录用户的关注的key不为空
        if (flag1) {
            //则获取当前页面用户的value，如果分数为0则代表没有关注
            fanScore = zset.score(fansKey, followerVo.getEntityId().toString());
        }

        //如果当前用户是某人的粉丝，则获取数量
        if (flag2) {
            size = zset.size(entityKey);
        }

        //访问的是哪个用户
        modelMap.put("userManager", entityUser);

        // 根据当前页面用户分数和自己的分数判断是否已经关注过当前页面的用户了
        modelMap.put("hasFollowed", score != null && score != 0);

        // 它关注了多少人
        modelMap.put("followerCount", size != null ? size : 0);

        // 它有多少粉丝
        modelMap.put("fansCount", count != null ? count : 0);

        return "site/profile";
    }
}
